package service;

import entity.User;
import utils.datastructureZ.binaryTree.RedBlackTree;

import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RankingServiceImp implements RankingService {

    /**
     * 排名索引中的用户分数节点
     */
    private static class UserPointNode implements Comparable<UserPointNode> {
        private long          point;
        private TreeSet<User> users;

        public UserPointNode(long point, User firstUser) {
            this.point = point;
            if(firstUser != null) {
                this.users = new TreeSet<>();
                users.add(firstUser);
            }
        }

        public void resetPoint(long newPoint) {
            if(users.size() > 1) throw new IllegalStateException("还有更多的用户等于该分数，不允许直接重设该分数。");
            else point = newPoint;
        }

        public TreeSet<User> getUsers() {
            return users;
        }

        public long getPoint() {
            return point;
        }

        public int getUsersCount() {
            return users.size();
        }

        /**
         * 增加一名用户到该分数节点下
         * @param user 目标用户
         * @return     增加后的用户数目
         */
        public int add(User user) {
            users.add(user);
            return users.size();
        }

        /**
         * 从该分数节点移除一名用户
         * @param user 目标用户
         * @return     剩余的用户数目
         */
        public int remove(User user) {
            users.remove(user);
            return users.size();
        }

        @Override
        public int compareTo(UserPointNode o) {
            return (int)(o.point - point);
        }

        @Override
        public String toString() {
            return "UserPointNode{" +
                "point=" + point +
                ", users=" + users +
            '}';
        }
    }

    /*****************************************************/
    private ReentrantReadWriteLock        rwLock;

    private ConcurrentHashMap<User, Long> userPool;
    private RedBlackTree<UserPointNode>   rankingIndex;
    /*****************************************************/
    private static RankingService RANKING_SERVICE = null;
    ///////////////////////////////////////////////////////

    private RankingServiceImp() {
        rwLock       = new ReentrantReadWriteLock();
        userPool     = new ConcurrentHashMap<>();
        rankingIndex = new RedBlackTree<>();
    }

    private RankingServiceImp(List<User> users) {
        this();
        rwLock.writeLock().lock();
        for(User user : users) {
            put(user);
        }
        rwLock.writeLock().unlock();
    }

    public static RankingService getRankingServiceImp(List<User> users) {
        if(users == null) return null;
        if(RANKING_SERVICE != null) return RANKING_SERVICE;
        return RANKING_SERVICE = new RankingServiceImp(users);
    }

    @Override
    public boolean put(User user) {

        Long pre = userPool.put(user, user.getScore());

        if(pre == null) { //新入榜的用户
            UserPointNode targetUserPointNode =
                    new UserPointNode(user.getScore(), user);

            rwLock.writeLock().lock();
            UserPointNode existUserPointNode =
                    rankingIndex.op_insert(targetUserPointNode);
            rwLock.writeLock().unlock();

            if(existUserPointNode != null) { //分数已存在
                existUserPointNode.add(user);
            }
            return true;
        } else { //用户已存在
            if(pre.equals(user.getScore())) return false; //用户的分数没有发生改变

            UserPointNode existUserNewPointNode = null;

            rwLock.writeLock().lock();
            UserPointNode existUserPointNode = rankingIndex.search(new UserPointNode(pre, null));

            if(existUserPointNode.getUsersCount() == 1) { //如果该分数下只剩最后一位（也就是当前的）用户

                rankingIndex.remove(existUserPointNode);
                existUserPointNode.resetPoint(user.getScore());

                //插入到新分数
                existUserNewPointNode = rankingIndex.op_insert(existUserPointNode);
            } else { //>1
                existUserPointNode.remove(user);
                //插入到新分数
                existUserNewPointNode = rankingIndex.op_insert(new UserPointNode(user.getScore(), user));
            }
            if(existUserNewPointNode != null) { //分数已存在
                existUserNewPointNode.add(user);
            }
            rwLock.writeLock().unlock();
        }
        return false;
    }

    @Override
    public boolean remove(User user) {
        Long pre = userPool.remove(user);
        if(pre == null) return false;

        rwLock.writeLock().lock();
        UserPointNode existUserPointNode = rankingIndex.search(new UserPointNode(pre, null));

        if(existUserPointNode == null) {
            rwLock.writeLock().unlock();
            return false;
        }
        if(existUserPointNode.getUsersCount() == 1) { //如果该分数下只剩最后一位（也就是当前的）用户
            rankingIndex.remove(existUserPointNode);
        } else existUserPointNode.remove(user);

        rwLock.writeLock().unlock();
        return true;
    }

    @Override
    public void update(User user) {
        put(user);
    }

    @Override
    public int size() {
        return userPool.size();
    }

    @Override
    public long getRank(User user) {
        Long rank = null;
        rwLock.readLock().lock();
        rank = rankingIndex.getRank(new UserPointNode(user.getScore(), null));
        rwLock.readLock().unlock();
        if(rank != 0) return rank;
        return -1L;
    }

    @Override
    public long getMaxRank() {
        Long rank = null;
        rwLock.readLock().lock();
        rank = rankingIndex.getMaxRank();
        rwLock.readLock().unlock();
        if(rank != 0) return rank;
        return -1L;
    }

    @Override
    public Long getPoint(User user) {
        return userPool.get(user);
    }

    @Override
    public TreeSet<User> getUsersByPoint(long point) {
        TreeSet<User> t = null;

        rwLock.readLock().lock();
        UserPointNode userPointNode = rankingIndex.search(new UserPointNode(point, null));
        if(userPointNode == null) {
            t = userPointNode.getUsers();
        }
        rwLock.readLock().unlock();
        return t;
    }

    @Override
    public TreeSet<User> getUsersByRank(long rank) {
        TreeSet<User> t = null;

        rwLock.readLock().lock();
        UserPointNode node = rankingIndex.searchByRank(rank);
        if(node != null) {
            t = node.getUsers();
        }
        rwLock.readLock().unlock();
        return t;
    }

    @Override
    public List<User> getUsersByRank(long low, long high) {
        if(low < 1L || low > high) return new LinkedList<>();

        LinkedList<User> set = new LinkedList<>();

        rwLock.readLock().lock();
        RedBlackTree<UserPointNode>.MidItr lower  = rankingIndex.locateByRank(low);
        RedBlackTree<UserPointNode>.MidItr higher = rankingIndex.locateByRank(high);
        while(lower.until(higher)) {
            set.addAll(lower.next().users);
        }
        rwLock.readLock().unlock();

        return set;
    }

    @Override
    public void dump() {

    }

    @Override
    public void load() {

    }

    @Override
    public void reset(List<User> users) {
        rwLock.writeLock().lock();

        userPool     = new ConcurrentHashMap<>();
        rankingIndex = new RedBlackTree<>();

        for(User user : users) {
            put(user);
        }

        rwLock.writeLock().unlock();
    }

    public static void main(String[] args) {
        RankingServiceImp testImp = new RankingServiceImp();

        User wangWu   = new User(123L, "王五", "王五", "123", null, 0, 0, 0, 567L, 1, "启用");
        User liSi     = new User(234L, "李四", "李四", "123", null, 0, 0, 0, 123L, 1, "启用");
        User zhangSan = new User(345L, "张三", "张三", "123", null, 0, 0, 0, 123L, 1, "启用");
        User wangLiu  = new User(456L, "王六", "王六", "123", null, 0, 0, 0, 678L, 1, "启用");

        User wangQi   = new User(789L, "王七", "王七", "123", null, 0, 0, 0, 789L, 1, "启用");
        User wangBa   = new User(788L, "王八", "王八", "123", null, 0, 0, 0, 345L, 1, "启用");
        User wangJiu  = new User(787L, "王九", "王九", "123", null, 0, 0, 0, 234L, 1, "启用");
        User wangShi  = new User(786L, "王十", "王十", "123", null, 0, 0, 0, 567L, 1, "启用");

        testImp.put(wangWu);
        testImp.put(liSi);
        testImp.put(zhangSan);
        testImp.put(wangLiu);

        testImp.put(wangQi);
        testImp.put(wangBa);
        testImp.put(wangJiu);
        testImp.put(wangShi);

        wangShi.setScore(34678L);
        testImp.put(wangShi);

        //User update = new User(787L, "王九", "王九", "123", null, 0, 0, 0, 235L, 1, "启用");
        //testImp.put(update);
/*
        System.out.println( "" +
            testImp.getRank(wangWu) + '\n' +
            testImp.getRank(liSi) + '\n' +
            testImp.getRank(zhangSan) + '\n' +
            testImp.getRank(wangLiu) + '\n' +

            testImp.getRank(wangQi) + '\n' +
            testImp.getRank(wangBa) + '\n' +
            testImp.getRank(wangJiu) + '\n' +
            testImp.getRank(wangShi) + '\n'
        );
*/
        //testImp.rankingIndex.testPri();
        //testImp.rankingIndex.testPri();

        List<User> users = testImp.getUsersByRank(1L, 5L);
        for(User user: users) {
            System.out.println(user.getName() + ':' + user.getScore());
        }
    }
}
